package jsr292.java.lang.invoke;

import jsr292.weaver.Debug;
import jsr292.weaver.opt.OptimizerContext;
import jsr292.weaver.opt.OptimizerSnippets;
import jsr292.weaver.opt.OptimizerVarAllocator;


class MHDropper extends MethodHandle {
    private final MethodHandle mh;
    private final int position;
    private final int length;
    
    public MHDropper(MethodHandle mh, int position, int length, MethodType newType) {
        super(newType);
        this.mh = mh;
        this.position = position;
        this.length = length;
    }
    
    @Override
    public Object $invoke$(Object o1) throws Throwable {
        // assert position==0, length==1
        return mh.$invoke$();
    }
    
    @Override
    public Object $invoke$(Object o1, Object o2) throws Throwable {
        switch(position) {
        case 0:
            switch(length) {
            case 1:
                return mh.$invoke$(o2);
            case 2:
                return mh.$invoke$();
            }
            break;
        case 1:
            // assert length==1
            return mh.$invoke$(o1);
        }
        throw super.assertion();
    }
    
    @Override
    public Object $invoke$(Object o1, Object o2, Object o3) throws Throwable {
        switch(position) {
        case 0:
            switch(length) {
            case 1:
                return mh.$invoke$(o2, o3);
            case 2:
                return mh.$invoke$(o3);
            case 3:
                return mh.$invoke$();
            }
            break;
        case 1:
            switch(length) {
            case 1:
                return mh.$invoke$(o1, o3);
            case 2:
                return mh.$invoke$(o1);
            }
            break;
        case 2:
            // assert length==1
            return mh.$invoke$(o1, o2);
        }
        throw super.assertion();
    }
    
    @Override
    public Object $invoke$(Object o1, Object o2, Object o3, Object o4) throws Throwable {
        switch(position) {
        case 0:
            switch(length) {
            case 1:
                return mh.$invoke$(o2, o3, o4);
            case 2:
                return mh.$invoke$(o3, o4);
            case 3:
                return mh.$invoke$(o4);
            case 4:
                return mh.$invoke$();
            }
            break;
        case 1:
            switch(length) {
            case 1:
                return mh.$invoke$(o1, o3, o4);
            case 2:
                return mh.$invoke$(o1, o4);
            case 3:
                return mh.$invoke$(o1);
            }
            break;
        case 2:
            switch(length) {
            case 1:
                return mh.$invoke$(o1, o2, o4);
            case 2:
                return mh.$invoke$(o1, o2);
            }
            break;
        case 3:
            // assert length==1
            return mh.$invoke$(o1, o2, o3);
        }
        throw super.assertion();
    }
    
    @Override
    public Object $invoke$(Object o1, Object o2, Object o3, Object o4, Object o5) throws Throwable {
        switch(position) {
        case 0:
            switch(length) {
            case 1:
                return mh.$invoke$(o2, o3, o4, o5);
            case 2:
                return mh.$invoke$(o3, o4, o5);
            case 3:
                return mh.$invoke$(o4, o5);
            case 4:
                return mh.$invoke$(o5);
            case 5:
                return mh.$invoke$();
            }
            break;
        case 1:
            switch(length) {
            case 1:
                return mh.$invoke$(o1, o3, o4, o5);
            case 2:
                return mh.$invoke$(o1, o4, o5);
            case 3:
                return mh.$invoke$(o1, o5);
            case 4:
                return mh.$invoke$(o1);
            }
            break;
        case 2:
            switch(length) {
            case 1:
                return mh.$invoke$(o1, o2, o4, o5);
            case 2:
                return mh.$invoke$(o1, o2, o5);
            case 3:
                return mh.$invoke$(o1, o2);
            }
            break;
        case 3:
            switch(length) {
            case 1:
                return mh.$invoke$(o1, o2, o3, o5);
            case 2:
                return mh.$invoke$(o1, o2, o3);
            }
            break;
        case 4:
            // assert length==1
            return mh.$invoke$(o1, o2, o3, o4);
        }
        throw super.assertion();
    }
    
    @Override
    public Object $invoke$(Object o1, Object o2, Object o3, Object o4, Object o5, Object o6) throws Throwable {
        switch(position) {
        case 0:
            switch(length) {
            case 1:
                return mh.$invoke$(o2, o3, o4, o5, o6);
            case 2:
                return mh.$invoke$(o3, o4, o5, o6);
            case 3:
                return mh.$invoke$(o4, o5, o6);
            case 4:
                return mh.$invoke$(o5, o6);
            case 5:
                return mh.$invoke$(o6);
            case 6:
                return mh.$invoke$();
            }
            break;
        case 1:
            switch(length) {
            case 1:
                return mh.$invoke$(o1, o3, o4, o5, o6);
            case 2:
                return mh.$invoke$(o1, o4, o5, o6);
            case 3:
                return mh.$invoke$(o1, o5, o6);
            case 4:
                return mh.$invoke$(o1, o6);
            case 5:
                return mh.$invoke$(o1);
            }
            break;
        case 2:
            switch(length) {
            case 1:
                return mh.$invoke$(o1, o2, o4, o5, o6);
            case 2:
                return mh.$invoke$(o1, o2, o5, o6);
            case 3:
                return mh.$invoke$(o1, o2, o6);
            case 4:
                return mh.$invoke$(o1, o2);    
            }
            break;
        case 3:
            switch(length) {
            case 1:
                return mh.$invoke$(o1, o2, o3, o5, o6);
            case 2:
                return mh.$invoke$(o1, o2, o3, o6);
            case 3:
                return mh.$invoke$(o1, o2, o3);    
            }
            break;
        case 4:
            switch(length) {
            case 1:
                return mh.$invoke$(o1, o2, o3, o4, o6);
            case 2:
                return mh.$invoke$(o1, o2, o3, o4);    
            }
            break;
        case 5:
            // assert length==1
            return mh.$invoke$(o1, o2, o3, o4, o5);
        }
        throw super.assertion();
    }
    
    @Override
    public Object $invoke$(Object o1, Object o2, Object o3, Object o4, Object o5, Object o6, Object o7) throws Throwable {
        switch(position) {
        case 0:
            switch(length) {
            case 1:
                return mh.$invoke$(o2, o3, o4, o5, o6, o7);
            case 2:
                return mh.$invoke$(o3, o4, o5, o6, o7);
            case 3:
                return mh.$invoke$(o4, o5, o6, o7);
            case 4:
                return mh.$invoke$(o5, o6, o7);
            case 5:
                return mh.$invoke$(o6, o7);
            case 6:
                return mh.$invoke$(o7);
            case 7:
                return mh.$invoke$();
            }
            break;
        case 1:
            switch(length) {
            case 1:
                return mh.$invoke$(o1, o3, o4, o5, o6, o7);
            case 2:
                return mh.$invoke$(o1, o4, o5, o6, o7);
            case 3:
                return mh.$invoke$(o1, o5, o6, o7);
            case 4:
                return mh.$invoke$(o1, o6, o7);
            case 5:
                return mh.$invoke$(o1, o7);
            case 6:
                return mh.$invoke$(o1);
            }
            break;
        case 2:
            switch(length) {
            case 1:
                return mh.$invoke$(o1, o2, o4, o5, o6, o7);
            case 2:
                return mh.$invoke$(o1, o2, o5, o6, o7);
            case 3:
                return mh.$invoke$(o1, o2, o6, o7);
            case 4:
                return mh.$invoke$(o1, o2, o7);
            case 5:
                return mh.$invoke$(o1, o2);
            }
            break;
        case 3:
            switch(length) {
            case 1:
                return mh.$invoke$(o1, o2, o3, o5, o6, o7);
            case 2:
                return mh.$invoke$(o1, o2, o3, o6, o7);
            case 3:
                return mh.$invoke$(o1, o2, o3, o7);    
            case 4:
                return mh.$invoke$(o1, o2, o3);
            }
            break;
        case 4:
            switch(length) {
            case 1:
                return mh.$invoke$(o1, o2, o3, o4, o6, o7);
            case 2:
                return mh.$invoke$(o1, o2, o3, o4, o7);
            case 3:
                return mh.$invoke$(o1, o2, o3, o4);    
            }
            break;
        case 5:
            switch(length) {
            case 1:
                return mh.$invoke$(o1, o2, o3, o4, o5, o7);
            case 2:
                return mh.$invoke$(o1, o2, o3, o4, o5);
            }
            break;
        case 6:
            // assert length==1
            return mh.$invoke$(o1, o2, o3, o4, o5, o6);
        }
        throw super.assertion();
    }
    
    @Override
    public Object $invoke$(Object... rest) throws Throwable {
        int newLength = rest.length-length;
        Object[] newArray=ArrayUtils.copyOf(rest, newLength);
        System.arraycopy(rest, position+length, newArray, position, newArray.length-position);
        
        if (newLength<=7) {
            return mh.invokeWithArguments(newArray);
        }
        
        return mh.$invoke$(newArray);
    }
    
    @Override
    public boolean isWeavable(Class<?> classBeingRedefined) {
	return mh.isWeavable(classBeingRedefined);
    }
    @Override
    public void weave(OptimizerContext context, OptimizerVarAllocator allocator) {
        if (Debug.DEBUG) {
            Debug.checkAllocator(allocator, this);
        }
        
        OptimizerSnippets.drop(context, allocator, mh, position, length);
    }
}
